home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 8 / QRZ Ham Radio Callsign Database - Volume 8.iso / pc / files / t_unix / j109lxa4.tar / kernel.c < prev    next >
C/C++ Source or Header  |  1994-06-04  |  14KB  |  584 lines

  1. /* Non pre-empting synchronization kernel, machine-independent portion
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4.  
  5. #define    SUSPEND_PROC    1
  6. #undef    PROCTRACE    1    /* kernel debugging */
  7. #undef    PROCLOG        1    /* debugging */
  8.  
  9. #if    defined(PROCLOG) || defined(PROCTRACE)
  10. #include <stdio.h>
  11. #endif
  12. #ifdef MSDOS
  13. #include <dos.h>
  14. #endif
  15. #include <setjmp.h>
  16. #include "global.h"
  17. #include "mbuf.h"
  18. #include "proc.h"
  19. #include "timer.h"
  20. #include "socket.h"
  21. #include "daemon.h"
  22. #include "hardware.h"
  23.  
  24. #ifdef    PROCLOG
  25. FILE *proclog;
  26. FILE *proctrace;
  27. #endif
  28. int Stkchk = 0;
  29. struct proc *Curproc;        /* Currently running process */
  30. struct proc *Rdytab;        /* Processes ready to run (not including curproc) */
  31. struct proc *Waittab[PHASH];    /* Waiting process list */
  32. struct proc *Susptab;        /* Suspended processes */
  33. static struct mbuf *Killq;
  34.  
  35. static void addproc __ARGS((struct proc *entry));
  36. static void delproc __ARGS((struct proc *entry));
  37.  
  38. /* Create a process descriptor for the main function. Must be actually
  39.  * called from the main function!
  40.  * Note that standard I/O is NOT set up here.
  41.  */
  42. struct proc *
  43. mainproc(name)
  44. char *name;
  45. {
  46.     register struct proc *pp;
  47. #ifndef UNIX
  48. #ifndef AMIGA
  49.     register unsigned k;
  50.     register int16 *p;
  51. #endif
  52. #endif
  53.  
  54.     /* Create process descriptor */
  55.     pp = (struct proc *)callocw(1,sizeof(struct proc));
  56.  
  57.     /* Create name */
  58.     pp->name = strdup(name);
  59. #ifdef UNIX
  60. #ifdef linux
  61.     pp->stksize = 0xC0000000;
  62.     pp->stack = (void *) 0xC0000000;
  63. #else
  64.     pp->stksize = 0x80000000;
  65.     pp->stack = (void *) 0x80000000;
  66. #endif
  67. #else
  68. #ifndef    AMIGA
  69.     pp->stksize = _stklen / sizeof(int16);
  70.     pp->stack = (p = MK_FP(_SS,0));
  71.     k = _SP / sizeof(int16);
  72.         while(k-- > 0) *p++ = STACKPAT;
  73. #else
  74.     init_psetup(pp);
  75. #endif
  76. #endif
  77.     /* Make current */
  78.     pp->state = READY;
  79.     Curproc = pp;
  80.  
  81. #ifdef    PROCLOG
  82.     proclog = fopen("proclog",APPEND_TEXT);
  83.     proctrace = fopen("proctrace",APPEND_TEXT);
  84. #endif
  85.     return pp;
  86. }
  87. /* Create a new, ready process and return pointer to descriptor.
  88.  * The general registers are not initialized, but optional args are pushed
  89.  * on the stack so they can be seen by a C function.
  90.  */
  91. struct proc *
  92. newproc(name,stksize,pc,iarg,parg1,parg2,freeargs)
  93. char *name;        /* Arbitrary user-assigned name string */
  94. unsigned int stksize;    /* Stack size in words to allocate */
  95. void (*pc)__ARGS((int,void*,void*));        /* Initial execution address */
  96. int iarg;        /* Integer argument (argc) */
  97. void *parg1;        /* Generic pointer argument #1 (argv) */
  98. void *parg2;        /* Generic pointer argument #2 (session ptr) */
  99. int freeargs;        /* If set, free arg list on parg1 at termination */
  100. {
  101.     register struct proc *pp;
  102.     int i;
  103.  
  104.     if(Stkchk)
  105.         chkstk();
  106.  
  107.     /* Create process descriptor */
  108.     pp = (struct proc *)callocw(1,sizeof(struct proc));
  109.  
  110.     /* Create name */
  111.     pp->name = strdup(name);
  112.  
  113.     /* Allocate stack */
  114. #ifdef    AMIGA
  115.     stksize += 2000;    /* DOS overhead */
  116. #endif
  117. #ifdef UNIX
  118.     stksize += 1536;        /* curses (rflush()) overhead */
  119. #endif
  120.     pp->stksize = stksize;
  121.     if((pp->stack = (int16 *)malloc(sizeof(int16)*stksize)) == NULL){
  122.         free(pp->name);
  123.         free((char *)pp);
  124.         return NULLPROC;
  125.     }
  126.     /* Initialize stack for high-water check */
  127.     for(i=0;i<stksize;i++)
  128.         pp->stack[i] = STACKPAT;
  129.  
  130.     /* Do machine-dependent initialization of stack */
  131.     psetup(pp,iarg,parg1,parg2,pc);
  132.  
  133.     pp->freeargs = freeargs;
  134.     pp->iarg = iarg;
  135.     pp->parg1 = parg1;
  136.     pp->parg2 = parg2;
  137.     
  138.     /* Inherit creator's input and output sockets */
  139.     usesock(Curproc->input);
  140.     pp->input = Curproc->input;
  141.     usesock(Curproc->output);
  142.     pp->output = Curproc->output;
  143.  
  144. #ifdef UNIX
  145.     /*
  146.      * The old "curses" tty driver faked this, occasionally getting it
  147.      * not quire right (IMHO, but the DOS version did the same implicitly).
  148.      * The new one uses this pointer to get it "right".
  149.      *
  150.      * The session manager uses this because multiple sessions (potentially
  151.      * all of them!) can be simultaneously "current" (e.g. "xterm" session
  152.      * manager).  This is even more important with external sessions, which
  153.      * are *always* "current".
  154.      */
  155.     pp->session = Curproc->session;
  156. #endif
  157.  
  158.     /* Add to ready process table */
  159.     pp->state = READY;
  160.     addproc(pp);
  161.     return pp;
  162. }
  163.  
  164. /* Free resources allocated to specified process. If a process wants to kill
  165.  * itself, the reaper is called to do the dirty work. This avoids some
  166.  * messy situations that would otherwise occur, like freeing your own stack.
  167.  */
  168. void
  169. killproc(pp)
  170. register struct proc *pp;
  171. {
  172.     char **argv;
  173.  
  174.     if(pp == NULLPROC)
  175.         return;
  176.     /* Don't check the stack here! Will cause infinite recursion if
  177.      * called from a stack error
  178.      */
  179.  
  180.     if(pp == Curproc)
  181.         killself();    /* Doesn't return */
  182.  
  183.     /* Close any open sockets */
  184.     freesock(pp);
  185.  
  186.     close_s(pp->input);
  187.     close_s(pp->output);
  188.  
  189.     /* Stop alarm clock in case it's running */
  190.     stop_timer(&pp->alarm);
  191.  
  192.     /* Alert everyone waiting for this proc to die */
  193.     psignal(pp,0);
  194.  
  195.     /* Remove from appropriate table */
  196.     delproc(pp);
  197.  
  198. #ifdef    PROCLOG
  199.     fprintf(proclog,"id %lx name %s stack %u/%u\n",ptol(pp),
  200.         pp->name,stkutil(pp),pp->stksize);
  201.     fclose(proclog);
  202.     proclog = fopen("proclog",APPEND_TEXT);
  203.     proctrace = fopen("proctrace",APPEND_TEXT);
  204. #endif
  205.     /* Free allocated memory resources */
  206.     if(pp->freeargs){
  207.         argv = pp->parg1;
  208.         while(pp->iarg-- != 0)
  209.             free(*argv++);
  210.         free(pp->parg1);
  211.     }
  212.     free(pp->name);
  213.     free(pp->stack);
  214.     free(pp->outbuf);
  215.     free((char *)pp);
  216. }
  217. /* Terminate current process by sending a request to the killer process.
  218.  * Automatically called when a process function returns. Does not return.
  219.  */
  220. void
  221. killself()
  222. {
  223.     register struct mbuf *bp;
  224.  
  225.     if(Curproc != NULLPROC){
  226.         bp = pushdown(NULLBUF,sizeof(Curproc));
  227.         memcpy(bp->data,(char *)&Curproc,sizeof(Curproc));
  228.         enqueue(&Killq,bp);
  229.     }
  230.     /* "Wait for me; I will be merciful and quick." */
  231.     for(;;)
  232.         pwait(NULL);
  233. }
  234. /* Process used by processes that want to kill themselves */
  235. void
  236. killer(i,v1,v2)
  237. int i;
  238. void *v1;
  239. void *v2;
  240. {
  241.     struct proc *pp;
  242.     struct mbuf *bp;
  243.  
  244.     for(;;){
  245.         while(Killq == NULLBUF)
  246.             pwait(&Killq);
  247.         bp = dequeue(&Killq);
  248.         pullup(&bp,(char *)&pp,sizeof(pp));
  249.         free_p(bp);
  250.         if(pp != Curproc)    /* We're immortal */
  251.             killproc(pp);
  252.     }                        
  253. }
  254.  
  255. #ifdef    SUSPEND_PROC
  256. /* Inhibit a process from running */
  257. void
  258. suspend(pp)
  259. struct proc *pp;
  260. {
  261.     if(pp == NULLPROC)
  262.         return;
  263.     if(pp != Curproc)
  264.         delproc(pp);    /* Running process isn't on any list */
  265.     pp->state |= SUSPEND;
  266.     if(pp != Curproc)
  267.         addproc(pp);    /* pwait will do it for us */
  268.     else
  269.         pwait(NULL);
  270. }
  271. /* Restart suspended process */
  272. void
  273. resume(pp)
  274. struct proc *pp;
  275. {
  276.     if(pp == NULLPROC)
  277.         return;
  278.     delproc(pp);    /* Can't be Curproc! */
  279.     pp->state &= ~SUSPEND;
  280.     addproc(pp);
  281. }
  282. #endif    /* SUSPEND_PROC */
  283.  
  284. /* Wakeup waiting process, regardless of event it's waiting for. The process
  285.  * will see a return value of "val" from its pwait() call.
  286.  */
  287. void
  288. alert(pp,val)
  289. struct proc *pp;
  290. int val;
  291. {
  292.     if(pp == NULLPROC)
  293.         return;
  294. #ifdef    notdef
  295.     if((pp->state & WAITING) == 0)
  296.         return;
  297. #endif
  298. #ifdef    PROCTRACE
  299.     fprintf(stderr, "alert(%lx,%u) [%s]\n",ptol(pp),val,pp->name);
  300.     fflush(stderr);
  301. #endif
  302.     if(pp != Curproc)
  303.         delproc(pp);
  304.     pp->state &= ~WAITING;
  305.     pp->retval = val;
  306.     pp->event = 0;
  307.     if(pp != Curproc)
  308.         addproc(pp);
  309. }
  310.  
  311. /* Post a wait on a specified event and give up the CPU until it happens. The
  312.  * null event is special: it means "I don't want to block on an event, but let
  313.  * somebody else run for a while". It can also mean that the present process
  314.  * is terminating; in this case the wait never returns.
  315.  *
  316.  * Pwait() returns 0 if the event was signaled; otherwise it returns the
  317.  * arg in an alert() call. Pwait must not be called from interrupt level.
  318.  *
  319.  * Note that pwait can run with interrupts enabled even though it examines
  320.  * a few global variables that can be modified by psignal at interrupt time.
  321.  * These *seem* safe.
  322.  */
  323. int
  324. #ifdef PROTOTYPES
  325. pwait(volatile void *event)
  326. #else
  327. pwait(event)
  328. void *event;
  329. #endif
  330. {
  331.     register struct proc *oldproc;
  332.     int tmp;
  333.  
  334. #ifndef UNIX
  335.     extern int WDTick;
  336.     extern int WDCurr;
  337.  
  338.     WDCurr = WDTick;        /* reset watchdog timer value */
  339. #endif
  340.  
  341.     if(Curproc != NULLPROC){    /* If process isn't terminating */
  342.         if(Stkchk)
  343.             chkstk();
  344.  
  345.         if(event == NULL){
  346.             /* Special case; just give up the processor.
  347.              *
  348.              * Optimization: if nothing else is ready, just return.
  349.              */
  350.             if(Rdytab == NULLPROC){
  351.                 return 0;
  352.             }
  353.         } else {
  354.             /* Post a wait for the specified event */
  355.             Curproc->event = event;
  356.             Curproc->state = WAITING;
  357.         }
  358.         addproc(Curproc);
  359.     }
  360.     /* Look for a ready process and run it. If there are none,
  361.      * loop or halt until an interrupt makes something ready.
  362.      */
  363.     while(Rdytab == NULLPROC){
  364.         /* Give system back to upper-level multitasker, if any.
  365.          * Note that this function enables interrupts internally
  366.          * to prevent deadlock, but it restores our state
  367.          * before returning.
  368.          */
  369. #ifndef UNIX
  370.         kbint();    /***/
  371. #endif
  372.         giveup();
  373.     }
  374.     /* Remove first entry from ready list */
  375.     oldproc = Curproc;
  376.     Curproc = Rdytab;
  377.     delproc(Curproc);
  378.  
  379.     /* Now do the context switch.
  380.      * This technique was inspired by Rob, PE1CHL, and is a bit tricky.
  381.      *
  382.      * If the old process has gone away, simply load the new process's
  383.      * environment. Otherwise, save the current process's state. Then if
  384.      * this is still the old process, load the new environment. Since the
  385.      * new task will "think" it's returning from the setjmp() with a return
  386.      * value of 1, the comparison with 0 will bypass the longjmp(), which
  387.      * would otherwise cause an infinite loop.
  388.      */
  389. #ifdef    PROCTRACE
  390.     if(strcmp(oldproc->name,Curproc->name) != 0){
  391.           fprintf(stderr, "pwait -> %s(%d)\n",Curproc->name,!!Curproc->i_state);
  392.         fflush(stderr);
  393.     }
  394. #endif
  395.     /* Note use of comma operator to save old interrupt state only if
  396.      * oldproc is non-null
  397.      */
  398.     if(oldproc == NULLPROC
  399.      || (oldproc->i_state = istate(), setjmp(oldproc->env) == 0)){
  400.         /* We're still running in the old task; load new task context.
  401.          * The interrupt state is restored here in case longjmp
  402.          * doesn't do it (e.g., systems other than Turbo-C).
  403.          */
  404.         restore(Curproc->i_state);
  405.         longjmp(Curproc->env,1);
  406.     }
  407.     /* At this point, we're running in the newly dispatched task */
  408.     tmp = Curproc->retval;
  409.     Curproc->retval = 0;
  410.  
  411.     /* Also restore the true interrupt state here, in case the longjmp
  412.      * DOES restore the interrupt state saved at the time of the setjmp().
  413.      * This is the case with Turbo-C's setjmp/longjmp.
  414.      */
  415.     restore(Curproc->i_state);
  416.     return tmp;
  417. }
  418.  
  419. /* Make ready the first 'n' processes waiting for a given event. The ready
  420.  * processes will see a return value of 0 from pwait().  Note that they don't
  421.  * actually get control until we explicitly give up the CPU ourselves through
  422.  * a pwait(). Psignal may be called from interrupt level. It returns the
  423.  * number of processes that were woken up.
  424.  */
  425. int
  426. #ifdef PROTOTYPES
  427. psignal(volatile void *event,int n)
  428. #else
  429. psignal(event,n)
  430. void *event;    /* Event to signal */
  431. int n;        /* Max number of processes to wake up */
  432. #endif
  433. {
  434.     register struct proc *pp;
  435.     struct proc *pnext;
  436.     int i_state;
  437.     unsigned int hashval;
  438.     int cnt = 0;
  439.  
  440.     if(Stkchk)
  441.         chkstk();
  442.  
  443.     if(event == NULL)
  444.         return 0;        /* Null events are invalid */
  445.  
  446.     /* n = 0 means "signal everybody waiting for this event" */
  447.     if(n == 0)
  448.         n = 65535;
  449.  
  450.     hashval = phash(event);
  451.     i_state = dirps();
  452.     for(pp = Waittab[hashval];n != 0 && pp != NULLPROC;pp = pnext){
  453.         pnext = pp->next;
  454.         if(pp->event == event){
  455. #ifdef    PROCTRACE
  456.             if(i_state){
  457.                 fprintf(stderr, "psignal(%lx,%u) wake %lx [%s]\n",ptol(event),n,
  458.                  ptol(pp),pp->name);
  459.                 fflush(stderr);
  460.             }
  461. #endif
  462.             delproc(pp);
  463.             pp->state &= ~WAITING;
  464.             pp->retval = 0;
  465.             pp->event = NULL;
  466.             addproc(pp);
  467.             n--;
  468.             cnt++;
  469.         }
  470.     }
  471. #ifdef    SUSPEND_PROC
  472.     for(pp = Susptab;n != 0 && pp != NULLPROC;pp = pnext){
  473.         pnext = pp->next;
  474.         if(pp->event == event){
  475. #ifdef    PROCTRACE
  476.             if(i_state){
  477.                 fprintf(stderr, "psignal(%lx,%u) wake %lx [%s]\n",ptol(event),n,
  478.                  ptol(pp),pp->name);
  479.                 fflush(stderr);
  480.             }
  481. #endif /* PROCTRACE */
  482.             delproc(pp);
  483.             pp->state &= ~WAITING;
  484.             pp->event = 0;
  485.             pp->retval = 0;
  486.             addproc(pp);
  487.             n--;
  488.             cnt++;
  489.         }
  490.     }
  491. #endif    /* SUSPEND_PROC */
  492.     restore(i_state);
  493.     return cnt;
  494. }
  495.  
  496. /* Rename a process */
  497. void
  498. chname(pp,newname)
  499. struct proc *pp;
  500. char *newname;
  501. {
  502.     free(pp->name);
  503.     pp->name = strdup(newname);
  504. }
  505. /* Remove a process entry from the appropriate table */
  506. static void
  507. delproc(entry)
  508. register struct proc *entry;    /* Pointer to entry */
  509. {
  510.     int i_state;
  511.  
  512.     if(entry == NULLPROC)
  513.         return;
  514.  
  515.     i_state = dirps();
  516.     if(entry->next != NULLPROC)
  517.         entry->next->prev = entry->prev;
  518.     if(entry->prev != NULLPROC){
  519.         entry->prev->next = entry->next;
  520.     } else {
  521.         switch(entry->state){
  522.         case READY:
  523.             Rdytab = entry->next;
  524.             break;
  525.         case WAITING:
  526.             Waittab[phash(entry->event)] = entry->next;
  527.             break;
  528. #ifdef    SUSPEND_PROC
  529.         case SUSPEND:
  530.         case SUSPEND|WAITING:
  531.             Susptab = entry->next;
  532.             break;
  533. #endif
  534.         }
  535.     }
  536.     restore(i_state);
  537. }
  538. /* Append proc entry to end of appropriate list */
  539. static void
  540. addproc(entry)
  541. register struct proc *entry;    /* Pointer to entry */
  542. {
  543.     register struct proc *pp;
  544.     struct proc **head;
  545.     int i_state;
  546.  
  547.     if(entry == NULLPROC)
  548.         return;
  549.  
  550.     switch(entry->state){
  551.     case READY:
  552.         head = &Rdytab;
  553.         break;
  554.     case WAITING:
  555.         head = &Waittab[phash(entry->event)];
  556.         break;
  557. #ifdef    SUSPEND_PROC
  558.     case SUSPEND:
  559.     case SUSPEND|WAITING:
  560.         head = &Susptab;
  561.         break;
  562. #endif
  563. #ifdef __GNUC__
  564.         default:
  565.         head = 0;    /* silence warning */
  566.         break;
  567. #endif
  568.     }
  569.     entry->next = NULLPROC;
  570.     i_state = dirps();
  571.     if(*head == NULLPROC){
  572.         /* Empty list, stick at beginning */
  573.         entry->prev = NULLPROC;
  574.         *head = entry;
  575.     } else {
  576.         /* Find last entry on list */
  577.         for(pp = *head;pp->next != NULLPROC;pp = pp->next)
  578.             ;
  579.         pp->next = entry;
  580.         entry->prev = pp;
  581.     }
  582.     restore(i_state);
  583. }
  584.